---
title: Report save to disk
summary: Writes a rendered report's bytes to a destination file, protecting existing files from accidental overwrite.
status: draft
owner: SAIF Documentation
last_reviewed: 2026-09-01
library: SAIF.Services.Printing
reference_type: embedded
operation_id: op-09-report-save-to-disk
trigger: Caller invokes ReportBase.Save
---

# Report save to disk

Renders a report (reusing a cached render where possible) and writes the resulting bytes to a caller-specified destination file, refusing to replace an existing file unless the caller explicitly requests an overwrite.

| Field | Value |
| --- | --- |
| **Operation ID** | OP-09 |
| **Trigger** | `Save` invoked |
| **Inputs** | `overwrite As Boolean = False`, `DestinationFileName` (instance property) |
| **Outputs** | `Boolean` — whether the destination file exists after the call |
| **Primary components** | `ReportBase.Save`, `Render`, `IO.FileStream` |
| **Entry point** | `Public Function Save(Optional overwrite As Boolean = False) As Boolean` |

## Steps

1. Checks whether `DestinationFileName` already exists on disk (`IO.File.Exists`).
2. Proceeds only if the file does **not** already exist, or `overwrite` is `True` — an existing destination file is not replaced unless the caller requests an overwrite ([BR-PRT-014](../business-rules.md#br-prt-014-destination-file-overwrite-protection)).
   - If neither condition holds: execution skips straight to step 5's existence check without touching the file.
3. Requires `m_DestinationFileName.Length > 0` before actually writing — saving requires a destination file name ([BR-PRT-014](../business-rules.md#br-prt-014-destination-file-overwrite-protection)).
   - If empty: no render and no write occurs, but execution still falls through to step 5.
4. Calls `Render(overwrite)` to ensure `m_reportFile` is populated (respecting `Render`'s own render-result caching), then opens `DestinationFileName` with `FileMode.Create` (which itself truncates/overwrites at the OS level) and writes all of `m_reportFile`'s bytes.

   > **Origin**: This step is the full [Report render lifecycle](op-06-report-render-lifecycle.md) operation, including its render-result caching ([BR-PRT-007](../business-rules.md#br-prt-007-render-result-caching)).
5. Returns `IO.File.Exists(DestinationFileName)` — re-checked fresh, not a cached flag carried over from step 1 — as the final result.

## Process flow

```mermaid
flowchart TD
	Start([Save called]) --> Check["Check file exists"]
	Check --> Guard{"exists AND<br>not overwrite?"}
	Guard -->|Yes| SkipWrite["skip write"]
	Guard -->|No| Named{"DestinationFileName<br>set?"}
	Named -->|No| SkipWrite
	Named -->|Yes| Render["Render()<br>(→OP-06)"]
	Render --> Write["FileStream.Create,<br>write bytes"]
	Write --> Result(["Return File.Exists<br>(re-checked)"])
	SkipWrite --> Result
```

## Failure and edge-case behavior

- There is no try/catch around the `FileStream` write: an `IOException` (locked file, invalid path, disk full) propagates unhandled — a contrast with `Render`'s and `Distribute`'s caught-and-recorded failure pattern.
- If `Render` inside step 4 fails silently ([BR-PRT-010](../business-rules.md#br-prt-010-non-throwing-render-and-distribute-failures) — returns `False` with an `ErrorDetail` but no exception) and leaves `m_reportFile` as `Nothing`, the subsequent `fs.Write(m_reportFile, 0, m_reportFile.Length)` would throw a `NullReferenceException` — a real, uncaught crash path.*
- The final `IO.File.Exists` check means `Save` can return `True` even when nothing was written during this call, simply because a pre-existing file (from a prior run) is present and no overwrite was requested. This is the intended [BR-PRT-014](../business-rules.md#br-prt-014-destination-file-overwrite-protection) semantics, not a bug.

## Related documentation
- [Index](../index.md)
- [Overview](../overview.md)
- [Architecture](../architecture.md)
- [Dependencies](../dependencies.md)
- [Operations and processes](../operations-processes.md)
- [Business rules](../business-rules.md)
- [Configuration](../configuration.md)
- [API reference](../api/index.md)

> **Note:** Statements marked with * were inferred from code structure, naming conventions, or
> contextual evidence where direct confirmation was not available in source artifacts. Statements
> marked with ** could not be confirmed; treat as unknown until verified.

## Sources
- `SAIF.Services.Printing/Reporting/ReportBase.vb`
- `SAIF.Services.Printing/Reporting/IReport.vb`
